module ActiveTest #:nodoc:
  module Asserts #:nodoc:
    module Difference

      # Asserts that there is a numerical difference, of one by default, between
      # the output of (object).(method) before and after the block is called.
      #
      # Original idea: http://project.ioni.st/post/217#post-217
      # Reload addition: Ben Curtis
      # Duping addition: Mathew Toth
      #
      #     assert_difference(Publication, :count) do
      #       post :create, :publication => {...}
      #     end
      # 
      # For instances, you can use the reload flag to ensure a fresh record object:
      #
      #   assert_difference @item, :quantity, 1, true
      #     @cart.add_product!(@item.product.id)
      #   end
      # 
      def assert_difference(object, method = nil, difference = 1, reload = false)
        initial = (initial = object.send(method)).dup rescue initial
        yield
        object.reload if reload
        assert_equal initial + difference, object.send(method), "#{object}##{method}"
      end

      # Asserts that there is no numerical difference between the output of object.method
      # before and after the block.
      def assert_no_difference(object, method, reload = false, &block)
        assert_difference object, method, 0, reload, &block
      end

      # Asserts that the output of object.method is the same before and after the block.
      #
      #   assert_change @user, :title do
      #     @user.name = "Miss Bogart"
      #   end
      #
      def assert_change(object, method, reload = false)
        initial = (initial = object.send(method)).dup rescue initial
        yield
        object.reload if reload
        assert initial != object.send(method), "#{object}##{method}"
      end

      # Asserts that the output of object.method is the same before and after the block.
      def assert_no_change(object, method, reload = false)
        initial = (initial = object.send(method)).dup rescue initial
        yield
        object.reload if reload
        assert initial == object.send(method), "#{object}##{method}"
      end
      
    end
  end
end
